﻿using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading;
using DG.Common.Annotations;
using DG.Common.Settings;

namespace DG.Common.Net
{

    public class AsyncFileDownload : INotifyPropertyChanged
    {
        private string _downloadLink;
        private string _saveFilePath;
        private int _progress;
        private AsyncFileDownloadStatus _status;

        public string DownloadLink
        {
            get { return _downloadLink; }
            set
            {
                if (value == _downloadLink) return;
                _downloadLink = value;
                OnPropertyChanged("DownloadLink");
            }
        }

        public string SaveFilePath
        {
            get { return _saveFilePath; }
            set
            {
                if (value == _saveFilePath) return;
                _saveFilePath = value;
                OnPropertyChanged("SaveFilePath");
            }
        }

        /// <summary>
        /// Progesso do Download.
        /// Varia de 0 a 100. 
        /// </summary>
        public int Progress
        {
            get { return _progress; }
            set
            {
                if (value == _progress) return;
                _progress = value;
                OnPropertyChanged("Progress");
            }
        }

        public AsyncFileDownloadStatus Status
        {
            get { return _status; }
            set
            {
                if (value == _status) return;
                _status = value;
                OnPropertyChanged("Status");
            }
        }

        public Exception ErrorException
        {
            get { return _errorException; }
            set
            {
                if (Equals(value, _errorException)) return;
                _errorException = value;
                OnPropertyChanged("ErrorException");
            }
        }

        public AsyncFileDownload(string downloadLink, string saveFilePath)
        {
            DownloadLink = downloadLink;
            SaveFilePath = saveFilePath;

            Status = AsyncFileDownloadStatus.NotStarted;
        }

        public Thread Start()
        {
            if (Status == AsyncFileDownloadStatus.Started) return null;

            Status = AsyncFileDownloadStatus.Started;

            var t = new Thread(() =>
            {
                if (!DoDownload()) return;
                Progress = 100;
                Status = AsyncFileDownloadStatus.Finished;
            });

            t.Start();

            return t;
        }
        public void Cancel()
        {
            _cancelPending = true;
        }

        private bool _cancelPending;
        private Exception _errorException;

        private bool DoDownload()
        {
            try
            {
                var request = WebRequest.Create(DownloadLink);
                request.Proxy = null;

                var d = new FileInfo(SaveFilePath).Directory;
                if (d != null && !d.Exists) d.Create();

                if (File.Exists(SaveFilePath)) File.Delete(SaveFilePath);

                using (var responseFileDownload = (HttpWebResponse)request.GetResponse())
                using (var responseStream = responseFileDownload.GetResponseStream())
                using (var writeStream = new FileStream(SaveFilePath, FileMode.Create))
                {
                    if (responseStream == null)
                    {
                        Status = AsyncFileDownloadStatus.Error;
                        ErrorException = new NullReferenceException("responseStream == null");
                        return false;
                    }

                    var fileSize = responseFileDownload.ContentLength;
                    var totalSize = (int)(fileSize) / 1024; // Kbytes

                    const int length = 2048;
                    var buffer = new Byte[length];
                    var bytesRead = responseStream.Read(buffer, 0, length);
                    var bytes = bytesRead;

                    while (bytesRead > 0 && !_cancelPending)
                    {
                        writeStream.Write(buffer, 0, bytesRead);
                        bytesRead = responseStream.Read(buffer, 0, length);
                        bytes += bytesRead;

                        Progress = totalSize == 0 ? 100 : (bytes / 1024) * 100 / totalSize;
                    }
                }
                if (_cancelPending)
                {
                    if (File.Exists(SaveFilePath)) File.Delete(SaveFilePath);
                    Progress = 0;
                    Status = AsyncFileDownloadStatus.Cancelled;
                    return false;
                }

                ErrorException = null;
                return true;
            }
            catch (Exception ex)
            {
                Status = AsyncFileDownloadStatus.Error;
                ErrorException = ex;
                return false;
            }
        }
        
        public enum AsyncFileDownloadStatus
        {
            NotStarted,
            Started,
            Stopped,
            Cancelled,
            Finished,
            Error
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected void OnPropertyChanged(string propertyName = null)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        
    }

   
}
